package com.fsbm.ctech.maxis.ebpsbackend;

import com.checkfree.isolutions.customer.persist.*;
import com.checkfree.isolutions.persist.*;
import com.checkfree.isolutions.statement.*;
import com.checkfree.isolutions.statement.persist.*;
import com.checkfree.isolutions.utility.*;
import com.checkfree.isolutions.admin.*;
import com.checkfree.isolutions.streaming.*;
import com.checkfree.isolutions.admin.ObjectId;

import java.io.File;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Vector;
import java.util.Properties;


import java.io.*;
import java.text.*;
import java.util.*;
import java.util.Date;
import java.sql.*;

/**
 * Batch Program to write Notification required files. Reads objects from the CheckFree DB thru
 * API to build the CSV files. It will stream the CSV and will build the zip files for transfer.
 * Instantiates an FTP and telnet session with the SMTP Server to transfer files.
 * <p>
 * Maxis Backend Program - Deliver Bills in customers inbox for subscriptions or adhoc requests.
 *
 * @author Michael Ty Chan
 */

public class DeliverByAcc {
    static ConfigObject cfgObj = new ConfigObject();
    static int intFileSize = cfgObj.maxFileAmount;
    static String[][] strSubsArrayFileName = new String[intFileSize][intFileSize];
    static String[][] strAdhocArrayFileName = new String[intFileSize][intFileSize];
	static int maxThread = 1;

    public static void main(String argv[]) {
        String strFile      = "", deliveryOpt = "",strMaxThread="";
        for (int optind = 0; optind < argv.length; optind++) {
            if (argv[optind].toLowerCase().indexOf("-procfile") != -1) {
                strFile = argv[optind];
                strFile = strFile.substring(strFile.indexOf("=")+1);
            } else if (argv[optind].toLowerCase().indexOf("-deliveryoptions") != -1) {
                deliveryOpt = argv[optind];
                deliveryOpt = deliveryOpt.substring(deliveryOpt.indexOf("=")+1);
            } /*else if (argv[optind].toLowerCase().indexOf("-threadcount") != -1) {
                strMaxThread = argv[optind];
                strMaxThread = strMaxThread.substring(strMaxThread.indexOf("=")+1);
                maxThread = Integer.parseInt(strMaxThread);
            }*/ else if (argv[optind].toLowerCase().startsWith("-h")) {
                printUsage();
                System.exit(1);
            } else {
                break;
            }
        }
        // Main Program Call
        startMain(strFile,deliveryOpt);
        System.exit(0);
    }

    // Start of sub-routines
    public static void startMain(String strFile, String deliveryOpt) {
        java.util.Date startTime = new Date();
        java.util.Date startTransferTime = new Date();
        java.util.Date endTransferTime = new Date();
        // Start to call creation of CSV, PDF, body.txt, command file and zipping of files
        try{

            if((strFile.length() > 0)){
                runner("1", strFile);
            }

        	//  Start of Transfer Functionality //
        	java.util.Date endTime = new Date();

        	// Display start and end times of Execution //
        	cfgObj.write("\n-------------------------------------------------" +
        	             "\nStart of Execution : " + startTime +
        	             "\nEnd   of Execution : " + endTime, "Deliver");
        	} catch (Exception ex) {
        	    cfgObj.write("Process Name(runner)- " + ex.getClass() + " " + ex.getMessage(), "Deliver" + deliveryOpt);}
    	}


    	// Start of runner sub-routines
    	public static void runner(String option, String strFile) {
    	    try{

				//ArrayList arrListStatementRunIds = new ArrayList();
				ArrayList accNoList = new ArrayList();
				ArrayList arrListSplitStatementRunIds = new ArrayList();

				Vector vecSubsStatementIds = new Vector();
				Vector vecSubsribedStatementIds = new Vector();
				Vector vecAdhocStatementIds = new Vector();

				Vector vecSplitStatementIds = new Vector();


    	        cfgObj.write("Before the loop....", "Subscription");
				if(((strFile.length() > 0) && (!strFile.equals("NONE"))) && (option.equals("0") || option.equals("1"))){    // Subscription PDF Delivery (Main)
    	            cfgObj.write("Extracting account number for => " + strFile.trim(), "Subscription");
    	            accNoList = extractAccountNoList(strFile.trim());
    	            cfgObj.write("Done with account number extraction for => " + strFile.trim(), "Subscription");

					//vecSubsStatementIds = GetStatement.getAllStmtIds(arrListStatementRunIds);
					vecSubsStatementIds = GetStatement.getAllStmtIdsByAccountList(accNoList);
					vecSplitStatementIds = GetStatement.splitStatementIds(vecSubsStatementIds,"Subscription");

					// Vectors used to keep track of the no of threads
					Vector vecThreads = new Vector();
            	    Vector vecRunningThreads = new Vector();


            	    // Start of Subscription process
            	    int vSize = vecSplitStatementIds.size();
            	    if(vSize>0){
						cfgObj.write("Starting subscription processes...","Subscription");

		            	for(int i =0;i < vSize; i++){
							SplitStatementIdsDataBean dataBean= (SplitStatementIdsDataBean)vecSplitStatementIds.elementAt(i);
							strSubsArrayFileName[0][i] = dataBean.getStrFileName();
							SubscriptionThread subsThread = new SubscriptionThread(vecSubsStatementIds,dataBean);
							vecThreads.addElement(subsThread);
						}
							cfgObj.write("Done with adding the statement IDs to the threads...","Subscription");

							int ctr=0;

						while(true){
							if(vecThreads.size()<=0 && vecRunningThreads.size()<=0) break;

							for(int j=0;j<vecRunningThreads.size();j++){
								SubscriptionThread runningSubs = (SubscriptionThread)vecRunningThreads.elementAt(j);

								if (runningSubs.isAlive() == false){
									vecRunningThreads.removeElementAt(j);
								}
							}

							if ((vecRunningThreads.size() < maxThread ) && (vecThreads.size() > 0)){
								SubscriptionThread subs = (SubscriptionThread)vecThreads.firstElement();
								subs.start();
								vecRunningThreads.addElement(subs);
								vecThreads.removeElement(subs);
							}
							try{
								Thread.sleep(1000);
							}catch(InterruptedException ie){
								//System.out.println("Exception is....."+ie);
							}
						}// end while

	                    cfgObj.write("Initialize Packaging...", "Subscription");
						PrepareFiles.PackageSubsDeliveryFiles(strSubsArrayFileName);                 // Call to create body/command and zip package
                    } else {
                        cfgObj.write("Process Name(runner) - There are no Statements found or Delivery Subscriptions.","Subscription");
                    }// end if
				}// end if


             	// Adhoc PDF Delivery (Main)
            	if (option.equals("0") || option.equals("2")){

					// Vector used to keep track of the no of thread
            	    Vector vecThreads = new Vector();
            	    Vector vecRunningThreads = new Vector();

            	    // Retrieve the statement IDs from custlog table
            	    vecAdhocStatementIds = GetAdhoc.retrieveStatementIds();                        // call to retrieve adhoc stmt ids

					if(vecAdhocStatementIds.size()>0){

						// Split all the statement IDs in to several files
						vecSplitStatementIds = GetStatement.splitStatementIds(vecAdhocStatementIds,"Adhoc");


            	    	cfgObj.write("\nRunning Adhoc processes...", "Adhoc");

            	    	int vSize = vecSplitStatementIds.size();
            	    	if (vSize>0){
            	    	    cfgObj.write("Creating files...", "Adhoc");

		            		for(int i =0;i < vSize; i++){
								SplitStatementIdsDataBean dataBean= (SplitStatementIdsDataBean)vecSplitStatementIds.elementAt(i);
								strAdhocArrayFileName[0][i] = dataBean.getStrFileName();
								AdhocThread adThread = new AdhocThread(vecAdhocStatementIds,dataBean);
								vecThreads.addElement(adThread);
							}

							while(true){
								if(vecThreads.size()<=0 && vecRunningThreads.size()<=0) break;

								for(int j=0;j<vecRunningThreads.size();j++){
									AdhocThread runningAds = (AdhocThread)vecRunningThreads.elementAt(j);

									if (runningAds.isAlive() == false){
										vecRunningThreads.removeElementAt(j);
									}
								}

								if ((vecRunningThreads.size() < maxThread ) && (vecThreads.size() > 0)){
									AdhocThread ads = (AdhocThread)vecThreads.firstElement();
									ads.start();
									vecRunningThreads.addElement(ads);
									vecThreads.removeElement(ads);
								}
								try{
									Thread.sleep(1000);
								}catch(InterruptedException ie){
									System.out.println("Exception is....."+ie);
								}
							}// end while

							cfgObj.write("Initialize Packaging...", "Adhoc");
							PrepareFiles.PackageAdhocDeliveryFiles(strAdhocArrayFileName);
						}// end if
					}else {
						cfgObj.write("Process Name(runner) - There are no Statements found for Adhoc Delivery .","Adhoc");
                    }// end if
				}
			}catch (Exception ex) {
				cfgObj.write("Process Name(runner)- " + ex.getClass() + " " + ex.getMessage(),"DeliverErr - " + option + ".");
			}//end try
		}// end class


		// Subscription Thread class
		public static class SubscriptionThread extends Thread {
			Vector vecStmtIds = new Vector();
			SplitStatementIdsDataBean dataBean = new SplitStatementIdsDataBean();

			SubscriptionThread(Vector stmtIds,SplitStatementIdsDataBean splitDataBean) {
				this.vecStmtIds = stmtIds;
				this.dataBean = splitDataBean;
			}

			public void run() {
		        FileOutputStream outstr = null;
		        InputStream instr   = null;
		        int intCommitCtr = 0;
		        boolean boolFirstLine = true;

		        // Setting the variables from the split statement IDs data bean
		        int intStartIdx = dataBean.getIntStartIdx();
		        int intEndIdx = dataBean.getIntEndIdx();
		        String strFileName = dataBean.getStrFileName();
				StatementIdsDataBean stmtIdDataBean = null;
				String strStmtId = "";

				try{
					FileWriter fwFile = null;
					String amountDue="";
            		int persisServerCtr = 0;

					PersistServer.activate();
					for(int i=intStartIdx;i<intEndIdx;i++){

	                    if(persisServerCtr == 0) {
							cfgObj.write("Activating Persistent Server.....","Subscription");
	                        PersistServer.beginTransaction();
							cfgObj.write("Starting the transaction.....","Subscription");
	                    }

						stmtIdDataBean = (StatementIdsDataBean)vecStmtIds.elementAt(i);

						AccountHome acctHome = AccountHomeImpl.singleton();                    // get account home
						StatementHome home = StatementHomeImpl.singleton();                  // get statement home

						strStmtId = stmtIdDataBean.getStmtId();
						int statementId = Integer.parseInt(strStmtId);        // convert string stmt Id to integer
						ObjectId objectId = new ObjectId(IClassId.STATEMENT, statementId);  // create an statement Object id based from the statement id string
		    	    	com.checkfree.isolutions.statement.persist.Statement cfiStatement   = home.findById(objectId);                        // exract statement object
    	            	Account accountObj = acctHome.findById(cfiStatement.getAccountId());
    	            	//AcctDetails acctDetails = new AcctDetails();

		    	    	// extract to string the ffg info for faster performance
		    	    	String acctId = stmtIdDataBean.getAcctId();
		    	    	String acctDeliveryEmail = stmtIdDataBean.getEmailAdd();
		    	    	amountDue = getFormattedAmount(cfiStatement.getUserField2());
		    	    	String accountName = cfiStatement.getUserField1();
		    	    	String billingPeriod = new SimpleDateFormat("dd/MM/yyyy").format(cfiStatement.getDate());
		    	    	accountName = accountName.replace(',', ' ');

		    	    	cfgObj.write("Checking Statement ID: " + statementId + " Account :" + acctId,"Subscription");

		    	    	int pos = strFileName.lastIndexOf("/");
		    	    	fwFile = new FileWriter(strFileName+".csv",true);

		    	    	cfgObj.write("1Checking Statement ID: " + statementId + " Account :" + acctId,"Subscription");
		    	    	if(boolFirstLine){
							fwFile.write(cfgObj.emailHeader1 + "," + cfgObj.emailHeader2 + "," + cfgObj.emailHeader3 + "," + cfgObj.emailHeader4 + "," + cfgObj.emailHeader5 + "," + cfgObj.emailHeader6 + "," + cfgObj.emailHeader7);
							boolFirstLine = false;
						}

		    	    	cfgObj.write("2Checking Statement ID: " + statementId + " Account :" + acctId,"Subscription");
						fwFile.write("\n"+billingPeriod + "," + acctDeliveryEmail + "," + accountName + "," + acctId + "," + amountDue + ',' + billingPeriod + "," + strFileName.substring(pos+1) + "." + statementId + "~" + acctId + ".pdf" );
						outstr        = new FileOutputStream(strFileName + "." + statementId + "~" + acctId + ".pdf");
						byte[] buffer = new byte[262144];
						int size      = 0;
		    	    	cfgObj.write("3Checking Statement ID: " + statementId + " Account :" + acctId,"Subscription");
						instr         = cfiStatement.getExternalFormatData();          // actual downloading of file in pdf
		    	    	cfgObj.write("4Checking Statement ID: " + cfiStatement + " Account :" + acctId,"Subscription");

	                    persisServerCtr++;

	                    if(persisServerCtr == 50) {
							// close transaction to avoid wasting memory
							cfgObj.write("Commiting transaction when ctr =50.......","Subscription");
							PersistServer.commitTransaction();
	                        persisServerCtr = 0;
	                    }

						while ((size  = instr.read(buffer, 0, buffer.length)) != -1) {
							outstr.write(buffer, 0, size);
						}
						instr.close();
						outstr.flush();
						outstr.close();
						fwFile.close();

						cfgObj.write("Found StmtID : " + statementId + " Account :" + acctId + " ->" + acctDeliveryEmail, "Subscription");

						if(i==intEndIdx-1){
							DBLog.createDeliveryRecord(strFileName.substring(pos+1) + ".csv", intEndIdx-intStartIdx,"SUBSCR");
						}

					}// end for

					if(persisServerCtr != 0) {
						cfgObj.write("Commiting transaction when ctr !=0.......","Subscription");
    	                PersistServer.commitTransaction();
    	            }

				}catch (Exception ex) {
					cfgObj.write("Process Name(execDeliverBySubscription)- for statement id(" +strStmtId+") - " + ex.getClass() + " " + ex.getMessage(), "Subscription");
					ex.printStackTrace();
				}finally {
					if (PersistServer.transactionIsActive()) {
						try {
							PersistServer.rollbackTransaction();
							cfgObj.write("Process Name(execDeliverBySubscription)- Rolling back all CheckFree transactions","Subscription");
						}catch (Exception e) {
							e.printStackTrace();
							//ignore
						}
					}
				PersistServer.deactivate(true); //clean up with deactivate
				}// end try
			}//end run
		}// end class


		// Adhoc Thread class
		public static class AdhocThread extends Thread {
			Vector vecStmtIds = new Vector();
			SplitStatementIdsDataBean dataBean = new SplitStatementIdsDataBean();

			AdhocThread(Vector stmtIds,SplitStatementIdsDataBean splitDataBean) {
				this.vecStmtIds = stmtIds;
				this.dataBean = splitDataBean;
			}

			public void run() {
		        FileOutputStream outstr = null;
		        InputStream instr   = null;
		        int intCommitCtr = 0;
		        boolean boolFirstLine = true;

		        int intStartIdx = dataBean.getIntStartIdx();
		        int intEndIdx = dataBean.getIntEndIdx();
		        String strFileName = dataBean.getStrFileName();
				try{
					PersistServer.activate();
					cfgObj.write("Activating Persistent Server.....","Adhoc");

					FileWriter fwFile = null;
            		int persisServerCtr = 0;

					for(int i=intStartIdx;i<intEndIdx;i++){
						String strStmtId = (String)vecStmtIds.elementAt(i);

	                    if(persisServerCtr == 0) {
	                        PersistServer.beginTransaction();
							cfgObj.write("Starting the transaction.....","Adhoc");
	                    }

						StatementHome home = StatementHomeImpl.singleton();
						AccountHome acctHome = AccountHomeImpl.singleton();
						String dbStmtId = strStmtId;                     // extract from vector of statement ids from the splitter

						String statementIdStr  = dbStmtId.substring(0, dbStmtId.indexOf("~"));      // format is stmtID~emailaddr
						String dbEmailAddress  = dbStmtId.substring(statementIdStr.length()+1);
						int statementId = Integer.parseInt(statementIdStr.trim());           // convert string stmtID to int
						ObjectId objectId = new ObjectId(IClassId.STATEMENT, statementId);     // create an statement Object id based from the statement id string
						com.checkfree.isolutions.statement.persist.Statement cfiStatement = home.findById(objectId);

						// extract to string the ffg info for a clearer view
						Account accountObj = acctHome.findById(cfiStatement.getAccountId());
						String acctId = accountObj.getExternalKey();
						String amountDue = getFormattedAmount(cfiStatement.getUserField2());
						String accountName = cfiStatement.getUserField1();
						String billingPeriod = new SimpleDateFormat("dd/MM/yyyy").format(cfiStatement.getDate());
						accountName = accountName.replace(',', ' ');

						cfgObj.write("Adhoc StmtID: " + statementId + " Account :" + acctId + " ->" + dbEmailAddress, "Adhoc");

						int pos = strFileName.lastIndexOf("/");
						fwFile = new FileWriter(strFileName+".csv",true);

						if(boolFirstLine){
							fwFile.write(cfgObj.emailHeader1 + "," + cfgObj.emailHeader2 + "," + cfgObj.emailHeader3 + "," + cfgObj.emailHeader4 + "," + cfgObj.emailHeader5 + "," + cfgObj.emailHeader6 + "," + cfgObj.emailHeader7);
							boolFirstLine = false;
						}

						fwFile.write("\n"+billingPeriod + "," + dbEmailAddress + "," + accountName + "," + acctId + "," + amountDue + ',' + billingPeriod + "," + strFileName.substring(pos+1) + "." + statementId + "~" + acctId + ".pdf" );

						outstr        = new FileOutputStream(strFileName + "." + statementId + "~" + acctId + ".pdf");
						byte[] buffer = new byte[262144];
						int size      = 0;
						instr         = cfiStatement.getExternalFormatData();          // actual downloading of file in pdf

	                    persisServerCtr++;

	                    if(persisServerCtr == 100) {
							// close transaction to avoid wasting memory
							PersistServer.commitTransaction();
							cfgObj.write("Commiting transaction when ctr =100.......","Adhoc");
	                        persisServerCtr = 0;
	                    }

						while ((size  = instr.read(buffer, 0, buffer.length)) != -1) {
							outstr.write(buffer, 0, size);
						}
						instr.close();
						outstr.flush();
						outstr.close();
						fwFile.close();

						if(i==intEndIdx-1){
							DBLog.createDeliveryRecord(strFileName.substring(pos+1) + ".csv", intEndIdx-intStartIdx,"ADHOC");
						}

					}// end for
					if(persisServerCtr != 0) {
						cfgObj.write("Commiting transaction when ctr !=0.......","Adhoc");
    	                PersistServer.commitTransaction();
    	            }


				}catch (Exception ex) {
					cfgObj.write("Process Name(execDeliverByAdhoc)- " + ex.getClass() + " " + ex.getMessage(), "Adhoc");
				}finally {
					if (PersistServer.transactionIsActive()) {
						try {
							PersistServer.rollbackTransaction();
		                    cfgObj.write("Process Name(execDeliverByAdhoc)- Rolling back all CheckFree transactions","Adhoc");
						}catch (Exception e) {
							//ignore
						}
					}
					PersistServer.deactivate(true); //clean up with deactivate
				}// end try
			}//end run
		}// end class


    	// Extract the statementrun IDs from the billProcFile
    	public static ArrayList extractStatementRunIds(String fileName){
    	    String lineFileReader    = "";
    	    ArrayList runIds         = new ArrayList();
    	    try {
    	        RandomAccessFile raf = new RandomAccessFile(fileName, "rw");
    	        boolean lnvalue      = true;
    	        String line          = "";
    	        int iCount           = 0;
    	        String[] strAccounts = new String[1024];
    	        while (lnvalue){
    	            line = raf.readLine();
    	            if (line == null){                                         //when end of file
    	                lnvalue      = false; }
    	            else{
    	                int srchPos1 = line.indexOf(",");
    	                if (srchPos1 != -1){
    	                    lineFileReader = line.substring(0, srchPos1);
    	                    runIds.add(lineFileReader.toString());
    	                }
    	            }
    	        }
    	        raf.close();
    	    }
    	    catch (Exception ex) {
    	        cfgObj.write("Process Name(extractStatementRunIds)- " + ex.getClass() + " " + ex.getMessage(),"Subscription");
    	    }
    	    return runIds;
    	}

    	// Extract the account numbers from the billProcFile
    	public static ArrayList extractAccountNoList(String fileName){
			ArrayList accNoList = new ArrayList();
			try{
				// Open the file that is the first
				// command line parameter
				FileInputStream fstream = new FileInputStream(fileName);

				// Get the object of DataInputStream
				DataInputStream in = new DataInputStream(fstream);
				BufferedReader br = new BufferedReader(new InputStreamReader(in));
				String strLine;

				//Read File Line By Line
				while ((strLine = br.readLine()) != null)   {
					// Print the content on the console
					//System.out.println (strLine);
					accNoList.add(strLine);
				}
				//Close the input stream
				in.close();
			}catch (Exception e){//Catch exception if any
				System.err.println("Error: " + e.getMessage());
			}
			return accNoList;
		}

    	public static void printUsage() {
    	      System.out.println("\nCopyright (c) 2002 FSBM CTech Sdn Bhd (427095-M).");
    	      System.out.println("All rights reserved.");
    	      System.out.println("\n\nUsage : deliver.sh [-DeliveryOptions=0|1|2] [-ProcFile=FILENAME]");
    	      System.out.println("or :");
    	      System.out.println("\tjava -server -Xms[HS] -Xmx[HS] -DBGROOT=/app/cfi Deliver");
    	      System.out.println("\t[-DeliveryOptions=0|1|2] [-ProcFile=FILENAME]");
    	      System.out.println("\n\nWhere:");
    	      System.out.println("\t -[FILENAME] where file.txt is the output of billproc");
    	      System.out.println("\t -[HS] Min and Max Heap Memory size allocation");
    	      System.out.println("\t -[0] Subscription and Adhoc requests(procFile param req'd)");
    	      System.out.println("\t -[1] Subscription only(procFile option req'd)");
    	      System.out.println("\t -[2] Adhoc request only");
    	      System.out.println("\n\tor java Deliver -h for help options");
    	      System.out.println("\tor deliver.sh -h for help options");
    	 }

    	public static String getFormattedAmount(String amountDue) {
    	            amountDue    = amountDue.replace(',',' ');
    	            StringTokenizer stAmountDue = new StringTokenizer(amountDue," ",false);
    	            amountDue    = "";
    	            while (stAmountDue.hasMoreElements()) amountDue += stAmountDue.nextElement();
    	    return amountDue;
    	}


	} // end class Deliver

	/* Use for debugging ... do not delete.
	        for (int i = 0; i < myFiles[1].length; i++){
	            System.out.println(myFiles[1][i]);
	        }
	 */

